package com.vmj.natives.sun.reflect;

import com.vmj.heap.VMClass;
import com.vmj.heap.Method;
import com.vmj.heap.VMObject;
import com.vmj.instructions.base.MethodInvokeLogic;
import com.vmj.natives.NativeMethod;
import com.vmj.natives.Registry;
import com.vmj.rtda.Frame;
import com.vmj.rtda.LocalVars;
import com.vmj.rtda.OperandStack;
import com.vmj.rtda.Thread;

/**
 * Created by yangshoujun on 2017/9/24 下午12:40.
 */
public class NativeConstructorAccessorImpl {

    public static final java.lang.String jlClass = "sun/reflect/NativeConstructorAccessorImpl";

    public static void init() {
        Registry.register(jlClass, "newInstance0", "(Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)Ljava/lang/Object;", new NativeMethod() {
            public void execute(Frame frame) {
                LocalVars vars = frame.getLocalVars();
                VMObject constructorObj = vars.getRef(0);
                VMObject argArrObj = vars.getRef(1);

                Method constructorMethod = getMethod(constructorObj, true);
                VMClass clazz = constructorMethod.getClazz();
                if(!clazz.isInitStarted()) {
                    frame.revertNextPc();
                    VMClass.initClass(frame.getThread(), clazz);
                    return;
                }

                VMObject obj = clazz.newObject();
                OperandStack stack = frame.getOperandStack();
                stack.pushRef(obj);

                OperandStack ops = convertArgs(obj, argArrObj, constructorMethod);

                Thread thread = frame.getThread();
                Frame shimFrame = com.vmj.natives.java.lang.Class.createShimFrame(thread, ops);
                thread.pushFrame(shimFrame);
                MethodInvokeLogic.invokeMethod(shimFrame, constructorMethod);
            }
        });
    }

    private static Method getMethod(VMObject methodObj, boolean isConstructor) {
        java.lang.Object extra = methodObj.getExtra();
        if(extra != null) {
            return (Method) extra;
        }

        if(isConstructor) {
            VMObject root = methodObj.getRefVar("root", "Ljava/lang/reflect/Constructor;");
            return (Method) root.getExtra();
        } else {
            VMObject root = methodObj.getRefVar("root", "Ljava/lang/reflect/Method;");
            return (Method) root.getExtra();
        }
    }

    private static OperandStack convertArgs(VMObject _this, VMObject argArr, Method method) {
        if(method.getArgSlotCount() == 0) {
            return null;
        }

        OperandStack ops = new OperandStack(method.getArgSlotCount());
        if(!method.isStatic()) {
            ops.pushRef(_this);
        }

        if(method.getArgSlotCount() == 1 && !method.isStatic()) {
            return ops;
        }

        return ops;
    }
}
